/** 
    @file pixelplayer.hpp
    @brief Player
*/

#ifndef PM3_PIXELPLAYER_HPP
#define PM3_PIXELPLAYER_HPP

#include "util.hpp"
#include "interface.hpp"
#include "camera_factory.hpp"
#include "physics.hpp"
#include "pixelmap.hpp"

#include <OgreSceneManager.h>
#include <OgreSceneNode.h>
#include <OgreRoot.h>

namespace pm3 {
    /**
        @class PIXELPLAYER
        @brief Player
    */
    class PIXELPLAYER: public LUA_SCRIPTED {
    friend class PIXELWORLD;
    public:
            /**
                @brief Constructor
            */
            PIXELPLAYER();
            /**
                @brief Destructor
            */
            ~PIXELPLAYER();

            /**
                @brief Unloads the map
            */
            void unload();

            /**
                @brief Adds new tags to the player
                @param[in] r_tags Reference to a list of tags (separated by commas with no whitespaces)
            */
            inline void add_tags( const std::string &r_tags ) {
                m_tags = pm3::util::add_tags( m_tags, r_tags );
            }

            /**
                @brief Removes tags from the player
                @param[in] r_tags Reference to a list of tags (separated by commas with no whitespaces)
            */
            inline void remove_tags( const std::string &r_tags ) {
                m_tags = pm3::util::remove_tags( m_tags, r_tags );
            }

            /**
                @brief Gets a list of tags associated with the player
                @return List of tags
            */
            inline std::string get_tags() const {
                return m_tags;
            }

            /**
                @brief Gets a pointer to the camera of the player
                @return Camera pointer
            */
            inline Ogre::PCZCamera *get_camera() const {
                return m_camera;
            }

            /**
                @brief Rotates the player around X and Y axes by specific angles
                @param[in] f_rot_x Angle by which to rotate around the X axis
                @param[in] f_rot_y Angle by which to rotate around the Y axis
            */
            void angle_rotate( Ogre::Degree f_rot_x, Ogre::Degree f_rot_y );

            /**
                @brief Moves the player
                @param[in] r_t Reference to translation vector
            */
            void move( const Ogre::Vector3 &r_t );

            /**
                @brief Stops player movement
            */
            void stop();

            /**
                @brief Snaps player to a new position
                @param[in] r_pos Reference to the new position
                @param[in] p_zone Pointer to the destination zone
            */
            void teleport( const Ogre::Vector3 &r_pos, Ogre::PCZone *p_zone );

            /**
                @brief Teleports player to a room
                @param[in] i_layer Destination layer
                @param[in] i_x Destination room X-coordinate
                @param[in] i_y Destination room Y-coordinate
                @return 0 on success and -1 on error
            */
            int teleport( int i_layer, int i_x, int i_y );

            /**
                @brief Sets player orientation
                @param[in] r_quat Reference to the quaternion
            */
            void set_orientation( const Ogre::Quaternion &r_quat );

            /**
                @brief Gets a string that describes player position and orientation
                @return The string
            */
            std::string get_pos_str() const;

            /**
                @brief Gets player position on the grid
                @return Player grid position
            */
            Ogre::Vector3 get_grid_pos() const;

            /**
                @brief Gets player world position
                @return Player world position
            */
            Ogre::Vector3 get_world_pos() const;

            /**
                @brief Sets player god mode
                @param[in] b_god Whether or not the player should be in god mode
            */
            void set_god_mode( bool b_god );

            /**
                @brief Checks if player is in god mode
                @return True if yes, false otherwise
            */
            inline bool get_god_mode() const {
                return m_god_mode;
            }

            /**
                @brief Sets mouse sensitivity
                @param[in] f_sensitivity Mouse sensitivity
            */
            inline void set_mouse_sensitivity( float f_sensitivity ) {
                m_mouse_sensitivity = f_sensitivity;
            }

            /**
                @brief Gets mouse sensitivity
                @return Mouse sensitivity
            */
            inline float get_mouse_sensitivity() const {
                return m_mouse_sensitivity;
            }

            /**
                @brief Sets player movement speed
                @param[in] f_speed Movement speed
            */
            inline void set_movement_speed( float f_speed ) {
                m_movement_speed = f_speed;
            }

            /**
                @brief Gets movement speed
                @return Movement speed
            */
            inline float get_movement_speed() const {
                return m_movement_speed;
            }

            /**
                @brief Sets player dimensions (for collision detection)
                @param[in] f_radius Player radius
                @param[in] f_height Player height
            */
            inline void set_player_dimensions( float f_radius, float f_height ) {
                m_radius = f_radius;
                m_height = f_height;
            }

            /**
                @brief Gets player collision radius
                @return Player collision radius
            */
            inline float get_radius() const {
                return m_radius;
            }

            /**
                @brief Gets player collision height
                @return Player collision height
            */
            inline float get_height() const {
                return m_height;
            }

            /**
                @brief Sets player mass
                @param[in] f_mass Player mass
            */
            inline void set_player_mass( float f_mass ) {
                m_mass = f_mass;
            }

            /**
                @brief Gets player mass
                @return Player mass
            */
            inline float get_mass() const {
                return m_mass;
            }

            /**
                @brief Finds the zone that the player is in
                @return Pointer to the zone
            */
            Ogre::PCZone *get_zone() const;

            /**
                @brief Gets a pointer to player node
                @return Pointer to player node
            */
            inline Ogre::PCZSceneNode *get_node() const {
                return m_camera_node;
            }

            /**
                @brief Dumps player parameters to the log
            */
            void dump() const;

            /**
                @brief Saves layer script As
                @param[in] r_path Path to the script
                @return Path to the script on success, empty string on failure
            */
            std::string save_script( const std::string &r_path );

            /**
                @brief Saves layer script
                @return Path to the script on success, empty string on failure
            */
            inline std::string save_script() {
                return save_script( m_script_path );
            }

            /**
                @brief Is player physics active?
                @return True if it is, otherwise false
            */
            inline bool get_physics_active() const {
                return m_physics_active;
            }

            /**
                @brief Activates or deactivates player physics
                @param[in] b_active True on active, false otherwise
            */
            void set_physics_active( bool b_active );

    protected:
            /**
                @brief Sets up player nodes, their rotation limits and so on..
            */
            void init();

            /**
                @brief Binds functions called by lua
            */
            void bind();

            /**
                @brief Sets the home zone of player nodes
                @param[in] p_zone Pointer to the new zone
            */
            void set_zone( Ogre::PCZone *p_zone );

    private:
            //! Camera node
            Ogre::PCZSceneNode *m_camera_node;
            //! Camera yaw node
            Ogre::PCZSceneNode *m_camera_yaw_node;
            //! Camera pitch node
            Ogre::PCZSceneNode *m_camera_pitch_node;
            //! Camera roll node
            Ogre::PCZSceneNode *m_camera_roll_node;

            //! Ogre camera pointer
            Ogre::PCZCamera *m_camera;

            //! Object state
            BtOgre::RigidBodyState *m_state;
            //! Pointer to rigid body
            btRigidBody *m_rigid_body;
            //! Pointer to collision shape
            btCollisionShape *m_collision_shape;

            //! Is player in god mode?
            bool m_god_mode;
            // Is player physics active?
            bool m_physics_active;

            //! Mouse sensitivity
            float m_mouse_sensitivity;
            //! Movement speed
            float m_movement_speed;
            //! Player height
            float m_height;
            //! Player radius
            float m_radius;
            //! Player mass
            float m_mass;

            /**
                @brief Sets up player body for animation and collision
            */
            void setup_body();

            //! Tags associated with player
            std::string m_tags;
    };

    //! Global instance of the player
    extern PIXELPLAYER *g_player;
}

#endif

